﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using nRoute.Components;
using nRoute.Utilities;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Markup;
using Windows.UI.Xaml.Media;

namespace nRoute.Behaviors.Interactivity
{
    //[ContentProperty(Name="Items")]
    public class AttachableCollection<T>
        : DependencyObjectCollection<T>, IAttachedObject
        where
            T : DependencyObject, IAttachedObject
    {
        private const string ITEM_ALREADY_EXISTS = "Item already exists in the collection";
        private const string DEPENDENCYOBJECT_CAN_ATTACHEDONCE = "IAttachedObject can only be attached once";

        private FrameworkElement _associatedObject;

#region IAttachedObject related

        FrameworkElement IAttachedObject.AssociatedObject
        {
            get { return _associatedObject; }
        }

        void IAttachedObject.Attach(FrameworkElement attachedObject)
        {
            Guard.ArgumentNotNull(attachedObject, "attachedObject");
            Guard.ArgumentValue(_associatedObject != null, "attachedObject", DEPENDENCYOBJECT_CAN_ATTACHEDONCE);

            _associatedObject = attachedObject;
            ((INotifyCollectionChanged)this).CollectionChanged += DependencyParametersCollection_CollectionChanged;

            this.OnAttached();
        }

        void IAttachedObject.Detach()
        {
            this.OnDetaching();

            ((INotifyCollectionChanged)this).CollectionChanged -= DependencyParametersCollection_CollectionChanged;
            _associatedObject = null;
        }

#endregion

#region Overridable

        protected virtual void ItemAdded(T item)
        {
            if (_associatedObject != null)
            {
                ((IAttachedObject)item).Attach(_associatedObject);
            }
        }

        protected virtual void ItemRemoved(T item)
        {
            if (_associatedObject != null)
            {
                ((IAttachedObject)item).Detach();
            }
        }

        protected FrameworkElement AssociatedObject
        {
            get { return _associatedObject; }
        }

        protected virtual void OnAttached() 
        {
            // no need to lock, as this should all happen on the UI thread
            foreach (IAttachedObject _item in this)
            {
                _item.Attach(_associatedObject);
            }
        }

        protected virtual void OnDetaching()
        {
            // no need to lock, as this should all happen on the UI thread
            foreach (IAttachedObject _item in this)
            {
                _item.Detach();
            }
        }

#endregion

#region Handlers

        private void DependencyParametersCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (T _item in e.NewItems)
                    {
                        VerifyAdd(_item);
                        ItemAdded(_item);
                    }
                    break;

                case NotifyCollectionChangedAction.Remove:
                    foreach (T _item in e.OldItems)
                    {
                        ItemRemoved(_item);
                    }
                    break;

                case NotifyCollectionChangedAction.Replace:
                    foreach (T _item in e.OldItems)
                    {
                        ItemRemoved(_item);
                    }
                    foreach (T _item in e.NewItems)
                    {
                        VerifyAdd(_item);
                        ItemAdded(_item);
                        continue;
                    }
                    break;

                case NotifyCollectionChangedAction.Reset:
                    foreach (T _item in this)
                    {
                        ItemRemoved(_item);
                    }
                    var _items = this.ToArray();
                    foreach (T _item in _items)
                    {
                        VerifyAdd(_item);
                        ItemAdded(_item);
                    }
                    break;

                default:
                    return;
            }
        }

#endregion

#region Helpers

        private void VerifyAdd(T item)
        {
            //if (base.Contains(item)) throw new InvalidOperationException(ITEM_ALREADY_EXISTS);
        }

#endregion

    }
}